package org.budo.mybatis.pagehelper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.budo.mybatis.Mybatis;
import org.budo.support.dao.page.Page;
import org.budo.support.jsqlparser.JSqlParserUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;

import com.github.pagehelper.PageHelper;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.select.OrderByElement;

/**
 * @author limingwei2
 */
public class BudoMybatisPageHelper {
    private static final Map<String, String> ORDER_BY_CACHE = new ConcurrentHashMap<String, String>();

    public static final ThreadLocal<Page> PAGE_THREAD_LOCAL = new ThreadLocal<Page>();

    public void startPage(Class<?> entityType, Page page, String sql) {
        if (null == page) {
            return;
        }

        int offset = page.getOffset();
        int pageSize = null == page.getPageSize() ? Page.PAGE_SIZE_DEFAULT : page.getPageSize();

        boolean count = null == page.getCount() || page.getCount(); // 默认要count

        com.github.pagehelper.Page<Object> helperPage = PageHelper.offsetPage(offset, pageSize);
        helperPage.setCount(count);

        String orderBy = page.getOrderBy();
        if (null != orderBy && !orderBy.trim().isEmpty()) {
            String _orderBy = this.fieldToColumnCached(entityType, orderBy); // 将 orderBy 里 的属性名 转为 列名
            helperPage.setOrderBy(_orderBy);
        } else {
            String _orderBy = this.defaultOrderBy(entityType, sql);
            helperPage.setOrderBy(_orderBy);
        }

        PAGE_THREAD_LOCAL.set(page);
    }

    /**
     * 如果未指定 的默认排序
     */
    protected String defaultOrderBy(Class<?> entityType, String sql) {
        String _sql = null == sql ? null : sql.toUpperCase();

        if (null != _sql && _sql.contains(" ORDER ") && _sql.contains(" BY ")) {
            return null; // 已手动指定 不默认
        }

        if (ReflectUtil.hasField(entityType, "index")) {
            return "`index` ASC"; // 指定排序
        }

        if (ReflectUtil.hasField(entityType, "id")) {
            return "`id` ASC"; // 默认主键倒序
        }

        return null;
    }

    protected String fieldToColumnCached(Class<?> entityType, String orderBy) {
        if (null == orderBy || orderBy.trim().isEmpty() || null == entityType) {
            return orderBy;
        }

        // JSqlParser 解析不了 类似 SELECT * FROM tj_tjk_workorder ORDER BY IF
        // (ISNULL(`update_time`), `create_time`, `update_time` ) DESC 的语句
        String orderByUpperCase = orderBy.replaceAll("\\s*", "").toUpperCase();
        if (orderByUpperCase.contains("IF(") || orderByUpperCase.contains("ISNULL(")) {
            return orderBy;
        }

        // 缓存
        String cacheKey = entityType + "-" + orderBy;
        String cacheValue = ORDER_BY_CACHE.get(cacheKey);
        if (null != cacheValue && !cacheValue.trim().isEmpty()) {
            return cacheValue;
        }

        // 解析
        String value = this.fieldToColumn(entityType, orderBy);
        ORDER_BY_CACHE.put(cacheKey, value);
        return value;
    }

    protected String fieldToColumn(Class<?> entityType, String orderBy) {
        List<OrderByElement> orderByElements = JSqlParserUtil.getOrderByElements(orderBy);

        List<String> orderByList = new ArrayList<String>();
        for (OrderByElement element : orderByElements) {
            Expression expression = element.getExpression();
            String column = Mybatis.fieldNameToColumn(entityType, expression + "");

            column = column.startsWith("`") || column.contains(".") // 已经有反引号的 或者 有点的 就不加 反引号
                    ? column //
                    : "`" + column + "`"; // 加上反引号

            orderByList.add(column + (element.isAsc() ? " ASC " : " DESC "));
        }

        return StringUtil.join(orderByList);
    }

    @SuppressWarnings("rawtypes")
    public static Number getRecordCount(List<?> list) {
        if (null != list && list instanceof com.github.pagehelper.Page) {
            return ((com.github.pagehelper.Page) list).getTotal();
        } else {
            return 0;
        }
    }
}